using ColorFillGame.Domain.Entities;
using ColorFillGame.Domain.Exceptions;
using ColorFillGame.Domain.ValueObjects;

namespace ColorFillGame.Domain.Services;

public class GameService : IGameService
{
    public Game CreateGame(string name, int gridSize, int maxPlayers, string customId = null)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new DomainException("Game name cannot be empty.");
        
        if (gridSize <= 0 || gridSize > 100)
            throw new DomainException("Grid size must be between 1 and 100.");
        
        if (maxPlayers <= 1 || maxPlayers > 8)
            throw new DomainException("Max players must be between 2 and 8.");
        
        // 使用自定义ID（如果提供），否则生成新的GUID
        Guid gameId;
        if (!string.IsNullOrEmpty(customId))
        {
            // 尝试将自定义ID转换为GUID
            if (!Guid.TryParse(customId, out gameId))
            {
                // 如果无法转换为GUID，使用字符串的哈希值生成GUID
                using (var md5 = System.Security.Cryptography.MD5.Create())
                {
                    var hashBytes = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(customId));
                    // 确保GUID的版本位正确
                    hashBytes[6] = (byte)((hashBytes[6] & 0x0F) | 0x30);
                    hashBytes[8] = (byte)((hashBytes[8] & 0x3F) | 0x80);
                    gameId = new Guid(hashBytes);
                }
            }
        }
        else
        {
            gameId = Guid.NewGuid();
        }
        
        return new Game(gameId, name, gridSize, maxPlayers);
    }

    public void JoinGame(Game game, Player player)
    {
        if (game == null)
            throw new ArgumentNullException(nameof(game));
        
        if (player == null)
            throw new ArgumentNullException(nameof(player));
        
        game.AddPlayer(player);
    }

    public void LeaveGame(Game game, Guid playerId)
    {
        if (game == null)
            throw new ArgumentNullException(nameof(game));
        
        game.RemovePlayer(playerId);
    }

    public void StartGame(Game game)
    {
        if (game == null)
            throw new ArgumentNullException(nameof(game));
        
        if (game.Players.Count < 2)
            throw new DomainException("At least 2 players are required to start the game.");
        
        game.StartGame();
    }

    public void PerformPaintAction(Game game, Player player, Position position)
    {
        if (game == null)
            throw new ArgumentNullException(nameof(game));
        
        if (player == null)
            throw new ArgumentNullException(nameof(player));
        
        if (position == null)
            throw new ArgumentNullException(nameof(position));
        
        game.ApplyPaintAction(player, position);
    }

    public bool CheckWinCondition(Game game, Player player)
    {
        if (game == null)
            throw new ArgumentNullException(nameof(game));
        
        if (player == null)
            throw new ArgumentNullException(nameof(player));
        
        if (player.Color == null)
            return false;
        
        var filledCells = game.Grid.GetFilledCellsByColor(player.Color);
        var totalCells = game.GridSize * game.GridSize;
        
        // Win condition: player has filled at least 50% of the grid
        return (double)filledCells.Count / totalCells >= 0.5;
    }

    public Dictionary<Player, int> CalculateScores(Game game)
    {
        if (game == null)
            throw new ArgumentNullException(nameof(game));
        
        var scores = new Dictionary<Player, int>();
        
        foreach (var player in game.Players)
        {
            if (player.Color != null)
            {
                var filledCells = game.Grid.GetFilledCellsByColor(player.Color);
                scores[player] = filledCells.Count;
            }
            else
            {
                scores[player] = 0;
            }
        }
        
        return scores;
    }

    public Player? GetPlayerWithMostCells(Game game)
    {
        if (game == null)
            throw new ArgumentNullException(nameof(game));
        
        var scores = CalculateScores(game);
        
        if (scores.Count == 0)
            return null;
        
        return scores.OrderByDescending(kvp => kvp.Value).First().Key;
    }
}